using System.Collections.Concurrent;

namespace FastDevTemplate.Service.Base.EventBus;

internal class MessageHandler
{
    private readonly Action<IMessage> _handleAction;

    public MessageHandler(Action<IMessage> handleAction, string key)
    {
        _handleAction = handleAction;
        Key = key;
    }

    internal string Key { get; set; }

    public void Handle(IMessage message)
    {
        _handleAction(message);
    }
}

public static class LocalEventBus
{
    private static readonly ConcurrentDictionary<Type, List<MessageHandler>> Handlers = new();

    /// <summary>
    ///     Publish message.
    /// </summary>
    /// <param name="message"></param>
    /// <param name="handlerID"></param>
    /// <typeparam name="TMessage"></typeparam>
    public static void Publish<TMessage>(TMessage message, string? handlerID = null) where TMessage : IMessage
    {
        var msgType = message.GetType();
        if (!Handlers.ContainsKey(msgType)) return;
        var handlerList = string.IsNullOrEmpty(handlerID)
            ? Handlers[msgType]
            : Handlers[msgType].Where(item => item.Key == handlerID);
        foreach (var action in handlerList)
            try
            {
                action.Handle(message);
            }
            catch (Exception e)
            {
                ErrorHandling(msgType.Name, "", e);
            }
    }

    /// <summary>
    ///     Subscribe message handle action.
    /// </summary>
    /// <typeparam name="TMessage"></typeparam>
    /// <param name="handler">message handle action.</param>
    /// <param name="handlerID">The default ID is automatically assigned .</param>
    /// <returns>subscribe id</returns>
    public static string Subscribe<TMessage>(Action<TMessage> handler, string? handlerID = null)
        where TMessage : IMessage
    {
        var msgType = typeof(TMessage);
        if (!Handlers.ContainsKey(msgType))
            Handlers[msgType] = new List<MessageHandler>();
        handlerID ??= Guid.NewGuid().ToString();
        Handlers[msgType].Add(new MessageHandler(msg => handler((TMessage)msg), handlerID));
        return handlerID;
    }

    /// <summary>
    ///     Unsubscribe message handle action.
    /// </summary>
    /// <param name="handlerID">subscribe id.</param>
    /// <typeparam name="TMessage"></typeparam>
    public static void Unsubscribe<TMessage>(string? handlerID = null)
        where TMessage : IMessage
    {
        var msgType = typeof(TMessage);
        if (!Handlers.ContainsKey(msgType)) return;
        if (string.IsNullOrEmpty(handlerID))
            Handlers[msgType].Clear();
        else
            Handlers[msgType].RemoveAll(x => x.Key == handlerID);
    }

    private static void ErrorHandling(string topic, string key, Exception? e = null)
    {
        var errorMsg = $"[{DateTime.Now}] Handle Error. Topic: {topic} Id: {key}";
        if (e != null)
            errorMsg += $" Msg: {e.Message}";
        Console.Error.WriteLine(errorMsg);
    }
}